﻿// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Copyright (c) 2011, Dr. Masroor Ehsan
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// $Author:$
// $Id:$
// $Rev:$
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

using System;
using System.Collections.Generic;
using System.Text;
using MaxTrader.BusinessObjects;
using MaxTrader.Chronology;

namespace MaxTrader.Quotes
{
    /// <summary>
    ///   TimeSeriesStockSnapshots is a specialized snapshot which contains contigious time-series 
    ///   based quotes for a specific stock. A regular snapshot (ISnapshot derivative classes) usually
    ///   contain many "holes" - i.e. periods during which the stock was not traded. This class will
    ///   fill up those holes - i.e. populate the gaps with zero volume. This class is intended to be 
    ///   used with a graphical stock chart.
    /// </summary>
    public class TimeSeriesStockSnapshots : ISnapshot
    {
        private readonly SortedList<DateTime, IDQuote> _quotes;

        public TimeSeriesStockSnapshots(string symbol, StockExchange exchange, DateTime beginTime, DateTime endTime,
                                        Timeframe timeFrame)
        {
            _quotes = new SortedList<DateTime, IDQuote>();

            Ticker = symbol;
            Exchange = exchange;
            StartTime = beginTime;
            EndTime = endTime;
            Period = timeFrame;

            InitializeTimeSlots();
        }

        public DateTime StartTime { get; private set; }

        public DateTime EndTime { get; private set; }

        public Timeframe Period { get; private set; }

        #region ISnapshot Members

        public string Ticker { get; private set; }

        public StockExchange Exchange { get; private set; }

        public decimal High { get; private set; }

        public decimal Low { get; private set; }

        public IEnumerable<IIDQuote> GetQuotes()
        {
            throw new NotImplementedException();
        }

        #endregion

        private void InitializeTimeSlots()
        {
            var curr = StartTime.TruncateSeconds();
            var end = EndTime.TruncateSeconds();

            while (curr <= end)
            {
                if ((Period >= Timeframe.Period1Min) && (Period <= Timeframe.Period30Min))
                {
                    AllocTimeSlot(curr);
                    curr = curr.AddMinutes(1);
                }
                else if ((Period >= Timeframe.PeriodHour) && (Period <= Timeframe.Period4Hour))
                {
                    AllocTimeSlot(curr);
                    curr = curr.AddHours(1);
                }
                else
                    switch (Period)
                    {
                        case Timeframe.PeriodDay:
                            AllocTimeSlot(curr);
                            curr = curr.AddDays(1);
                            break;
                        case Timeframe.PeriodWeek:
                            AllocTimeSlot(curr);
                            curr = curr.AddDays(7);
                            break;
                        case Timeframe.PeriodMonth:
                            AllocTimeSlot(curr);
                            curr = curr.AddMonths(1);
                            break;
                        case Timeframe.PeriodYear:
                            AllocTimeSlot(curr);
                            curr = curr.AddYears(1);
                            break;
                    }
            }
        }

        //TODO: investigate/fix this method
        private void AllocTimeSlot(DateTime date)
        {
            var period = TimeframeMappers.MapDateToPeriod(Period, date);
            if (!_quotes.ContainsKey(period))
            {
                var quote = new IDQuote(this) {UtcTimeStamp = period};
                _quotes.Add(period, quote);
            }
        }

        /// <summary>
        ///   Imports quotes from another snapshot. Normalize() must be called to fill up the "holes"
        /// </summary>
        public void AddQuotes(ISnapshot fromSnapshot)
        {
            var index = 0;
            foreach (var qNew in fromSnapshot.GetQuotes())
            {
                var period = TimeframeMappers.MapDateToPeriod(Period, qNew.UtcTimeStamp);
                if (!_quotes.ContainsKey(period))
                {
                    AllocTimeSlot(qNew.UtcTimeStamp);
                }
                var qOld = _quotes[period];

                if (qOld.IsEmpty())
                {
                    qOld.Price = qNew.Price;
                }
                else
                {
                    qOld.Price = (qOld.Price + qNew.Price)/2.0m;
                }

                if (index == 0)
                {
                    // the first entry
                    qOld.VolumeDifference = qNew.CumulativeVolume;
                }
                else
                {
                    qOld.VolumeDifference += qNew.VolumeDifference;
                }
                if (qOld.CumulativeVolume < qNew.CumulativeVolume)
                    qOld.CumulativeVolume = qNew.CumulativeVolume;
                index++;
            }

            High = fromSnapshot.High;
            Low = fromSnapshot.Low;

            Normalize();
        }

        /// <summary>
        ///   Fills the "holes" between subsequent quotes by inserting zero-volume
        ///   quotes
        /// </summary>
        public void Normalize()
        {
            IDQuote prevQuote = null;
            foreach (var currQuote in _quotes.Values)
            {
                if (currQuote.IsEmpty() && (prevQuote != null))
                {
                    currQuote.CloneFrom(prevQuote);
                }

                prevQuote = currQuote;
            }
        }

        //TODO: replace this with a better method
        public string DumpAll()
        {
            var sb = new StringBuilder();
            foreach (var snap in _quotes.Values)
            {
                sb.AppendLine(snap.ToString());
            }
            return sb.ToString();
        }
    }
}